/*
REWRITTEN BY XINEF
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "gruuls_lair.h"

enum Yells
{
    SAY_AGGRO                   = 0,
    SAY_SLAM                    = 1,
    SAY_SHATTER                 = 2,
    SAY_SLAY                    = 3,
    SAY_DEATH                   = 4,

    EMOTE_GROW                  = 5
};

enum Spells
{
    SPELL_GROWTH                = 36300,
    SPELL_CAVE_IN               = 36240,
    SPELL_GROUND_SLAM           = 33525,
    SPELL_REVERBERATION         = 36297,
    SPELL_HURTFUL_STRIKE        = 33813,
    SPELL_SHATTER               = 33654,

    SPELL_SHATTER_EFFECT        = 33671,
    SPELL_STONED                = 33652,
};

enum Events
{
    EVENT_GROWTH				= 1,
    EVENT_CAVE_IN				= 2,
    EVENT_GROUND_SLAM			= 3,
    EVENT_HURTFUL_STRIKE		= 4,
    EVENT_REVERBERATION			= 5,
	EVENT_SHATTER				= 6,
	EVENT_RECENTLY_SPOKEN		= 7
};

class boss_gruul : public CreatureScript
{
    public:
        boss_gruul() : CreatureScript("boss_gruul") { }

        struct boss_gruulAI : public BossAI
        {
            boss_gruulAI(Creature* creature) : BossAI(creature, DATA_GRUUL) { }

            void Reset()
            {
                _Reset();
				_caveInTimer = 29000;
            }

            void EnterCombat(Unit* /*who*/)
            {
                _EnterCombat();
                Talk(SAY_AGGRO);

				events.ScheduleEvent(EVENT_GROWTH, 30000);
				events.ScheduleEvent(EVENT_CAVE_IN, _caveInTimer);
				events.ScheduleEvent(EVENT_REVERBERATION, 20000);
				events.ScheduleEvent(EVENT_HURTFUL_STRIKE, 10000);
				events.ScheduleEvent(EVENT_GROUND_SLAM, 35000);
            }

            void KilledUnit(Unit* who)
            {
                if (events.GetNextEventTime(EVENT_RECENTLY_SPOKEN) == 0)
				{
					events.ScheduleEvent(EVENT_RECENTLY_SPOKEN, 5000);
                    Talk(SAY_SLAY);
				}
            }

            void JustDied(Unit* /*killer*/)
            {
                _JustDied();
                Talk(SAY_DEATH);
            }

            void UpdateAI(uint32 diff)
            {
                if (!UpdateVictim())
                    return;

                events.Update(diff);
                if (me->HasUnitState(UNIT_STATE_CASTING))
                    return;

				switch (events.ExecuteEvent())
				{
					case EVENT_GROWTH:
						Talk(EMOTE_GROW);
						DoCast(me, SPELL_GROWTH);
						events.ScheduleEvent(EVENT_GROWTH, 30000);
						break;
					case EVENT_CAVE_IN:
						if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
							me->CastSpell(target, SPELL_CAVE_IN, false);
						_caveInTimer -= 1500;
						events.ScheduleEvent(EVENT_CAVE_IN, _caveInTimer);
						break;
					case EVENT_REVERBERATION:
						me->CastSpell(me, SPELL_REVERBERATION, false);
						events.ScheduleEvent(EVENT_REVERBERATION, 22000);
						break;
					case EVENT_HURTFUL_STRIKE:
						if (Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 1, 5.0f))
							me->CastSpell(target, SPELL_HURTFUL_STRIKE, false);
						events.ScheduleEvent(EVENT_HURTFUL_STRIKE, 15000);
						break;
					case EVENT_GROUND_SLAM:
						Talk(SAY_SLAM);
						me->CastSpell(me, SPELL_GROUND_SLAM, false);
						events.DelayEvents(8001);
						events.ScheduleEvent(EVENT_GROUND_SLAM, 60000);
						events.ScheduleEvent(EVENT_SHATTER, 8000);
						break;
					case EVENT_SHATTER:
						Talk(SAY_SHATTER);
						me->CastSpell(me, SPELL_SHATTER, false);
						break;
				}

                DoMeleeAttackIfReady();
            }

			private:
				uint32 _caveInTimer;
        };

        CreatureAI* GetAI(Creature* creature) const
        {
            return GetInstanceAI<boss_gruulAI>(creature);
        }
};

class spell_gruul_ground_slam : public SpellScriptLoader
{
    public:
        spell_gruul_ground_slam() : SpellScriptLoader("spell_gruul_ground_slam") { }

        class spell_gruul_ground_slam_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_gruul_ground_slam_SpellScript);

            void HandleScriptEffect(SpellEffIndex /*effIndex*/)
            {
                if (Unit* target = GetHitUnit())
					target->KnockbackFrom(GetCaster()->GetPositionX(), GetCaster()->GetPositionY(), 15.0f, 15.0f);
            }

            void Register()
            {
                OnEffectHitTarget += SpellEffectFn(spell_gruul_ground_slam_SpellScript::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_gruul_ground_slam_SpellScript();
        }
};

class spell_gruul_ground_slam_trigger : public SpellScriptLoader
{
    public:
        spell_gruul_ground_slam_trigger() : SpellScriptLoader("spell_gruul_ground_slam_trigger") { }

        class spell_gruul_ground_slam_trigger_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_gruul_ground_slam_trigger_AuraScript);

            void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
            {
				if (GetUnitOwner()->GetAuraCount(GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell) == 5)
					GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_STONED, true);
            }

            void Register()
            {
                 AfterEffectRemove += AuraEffectRemoveFn(spell_gruul_ground_slam_trigger_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL);
            }
        };

        AuraScript* GetAuraScript() const
        {
            return new spell_gruul_ground_slam_trigger_AuraScript();
        }
};

class spell_gruul_shatter : public SpellScriptLoader
{
    public:
        spell_gruul_shatter() : SpellScriptLoader("spell_gruul_shatter") { }

        class spell_gruul_shatter_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_gruul_shatter_SpellScript);

            void HandleScriptEffect(SpellEffIndex /*effIndex*/)
            {
                if (Unit* target = GetHitUnit())
				{
                    target->RemoveAurasDueToSpell(SPELL_STONED);
                    target->CastSpell((Unit*)NULL, SPELL_SHATTER_EFFECT, true);
				}
            }

            void Register()
            {
                OnEffectHitTarget += SpellEffectFn(spell_gruul_shatter_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_gruul_shatter_SpellScript();
        }
};

class spell_gruul_shatter_effect : public SpellScriptLoader
{
    public:
        spell_gruul_shatter_effect() : SpellScriptLoader("spell_gruul_shatter_effect") { }

        class spell_gruul_shatter_effect_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_gruul_shatter_effect_SpellScript);

            void CalculateDamage()
            {
                if (!GetHitUnit())
                    return;

                float radius = GetSpellInfo()->Effects[EFFECT_0].CalcRadius(GetCaster());
                if (!radius)
                    return;

                float distance = GetCaster()->GetDistance2d(GetHitUnit());
                if (distance > 1.0f)
                    SetHitDamage(int32(GetHitDamage() * ((radius - distance) / radius)));
            }

            void Register()
            {
                OnHit += SpellHitFn(spell_gruul_shatter_effect_SpellScript::CalculateDamage);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_gruul_shatter_effect_SpellScript();
        }
};

void AddSC_boss_gruul()
{
    new boss_gruul();
	new spell_gruul_ground_slam();
	new spell_gruul_ground_slam_trigger();
    new spell_gruul_shatter();
    new spell_gruul_shatter_effect();
}
